/**********************************************************************
Compile : gcc -O3 -o generate5 check.c generate5.c
Usage : ./generate5 mode mode1 mode2 pnflag N L n seed       
mode  : 0 common; 1 cycled; 2: triggled cycle
mode1 : 0 disallow self-activation; 1 allow self-activation
mode2 : Additional Requirements: 0 no requirements; 1 restriction I; 2 restriction II; 3 restriction III
pnflag: 1 print the candidate processes
N     : number of the nodes                
L     : number of the steps        
n     : the number of the attempts         
seed  : random seed
***********************************************************************/

#include <stdio.h>
#include <stdlib.h>

int generate_pathway2 (int N, int L, int pathway[L][N], int mode)
{
	int i, j, k, l, select, n, flag, flag2;
	struct stable {
		int *index;
		int num;
	} stable, stable2;

	stable.index = calloc (N, sizeof (int));
	stable2.index = calloc (N, sizeof (int));

	do
	{
		flag = 0;
		for (i = 0; i < L; i++)
		{
			for (j = 0; j < N; j++)
			{
				pathway[i][j] = -1;
			}
		}
        
		for (i = 0;  i < L - 1; i++)
		{
			for (j = 0; j < N; j++)
			{
				if (pathway[i][j] == -1)
				{
					if (drand48 () > 0.5)
					{
						pathway[i][j] = 0;
					}
					else
					{
						pathway[i][j] = 1;
					}
				}
			}
        
			if (i == 0)
			{
				select = drand48 () * N;
				pathway[i + 1][select] = pathway[i][select];
			}
			else
			{
				stable.num = 0;
				for (j = 0; j < N; j++)
				{
					if (pathway[i][j] == pathway[i - 1][j])
					{
						stable.index[stable.num] = j;
						stable.num ++;
					}
				}
        
				if (stable.num == 0)
				{
					flag = 1;
					break;
				}
        
				for (j = i + 1; j < L - 1; j++)
				{
					flag2 = 0;
					stable2.num = 0;
					for (l = 0; l < stable.num; l++)
					{
						k = stable.index[l];
						if (pathway[j][k] != pathway[i][k] && pathway[j + 1][k] != pathway[i][k])
						{
							if (pathway[j][k] + pathway[i][k] == 1 && pathway[j + 1][k] + pathway[i][k] == 1)
							{
								flag2 = 1;
								break;
							}

							stable2.index[stable2.num] = k;
							stable2.num ++;
						}
					}
        
					if (flag2 == 1)
						continue;

					if (stable2.num == 0)
					{
						flag = 1;
						break;
					}
        
					select = drand48 () * stable2.num;
					select = stable2.index[select];
					pathway[j][select] = 1 - pathway[i][select];
					pathway[j + 1][select] = 1 - pathway[i][select];
				}

				if (flag == 1)
					break;
			}
		}
	}
	while (flag);
        
	for (i = 0; i < N; i++)
	{
		if (mode == 0)
			pathway[L - 1][i] = pathway[L - 2][i];
		else if (mode == 1)
			pathway[L - 1][i] = pathway[0][i];
	}
        
	n = 0;
	for (i = 0;  i < L - 1; i++)
	{
		for (j = 0; j < N; j++)
		{
			n += pathway[i][j];
		}
	}

	free (stable.index);
	free (stable2.index);

	return n;
}

int generate_pathway (int N, int L, int pathway[L][N], int mode)
{
	if (mode != 2)
		return generate_pathway2 (N, L, pathway, mode);
	else
	{
		int i, j, np;
		int pathway2[L - 1][N - 1];

		np = generate_pathway2 (N - 1, L - 1, pathway2, 0);

		pathway[0][0] = 1;
		for (i = 1; i < L; i++)
		{
			pathway[i][0] = 0;
		}

		for (i = 0; i < L - 1; i++)
		{
			for (j = 0; j < N - 1; j++)
			{
				pathway[i + 1][j + 1] = pathway2[i][j];
			}
		}

		for (i = 1; i < N; i++)
		{
			pathway[0][i] = pathway2[L - 2][i - 1];
		}

		return np;
	}
}

int check_duplicate (int N, int L, int pathway[L][N])
{
	int i, j, flag;

	for (i = 0; i < L - 2; i++)
	{
		flag = 1;
		for (j = 0; j < N; j++)
		{
			if (pathway[i][j] != pathway[i + 1][j])
			{
				flag = 0;
				break;
			}
		}
		if (flag == 1) break;
	}

	return flag;
}

int main (int argc, char *argv[])
{
	long int seed;
	int N, L, i, j, k, m, n, p, np;
	int mode, mode1, mode2, pnflag;

	mode = atoi (argv[1]);
	mode1 = atoi (argv[2]);
	mode2 = atoi (argv[3]);
	pnflag = atoi (argv[4]);
	N = atoi (argv[5]);
	L = atoi (argv[6]);
	n = atoi (argv[7]);
	seed = atoi (argv[8]);
	srand48 (seed);
	printf ("Random Seed : %ld\n", seed);

	long int counter1[(L - 1) * N + 1], counter2[(L - 1) * N + 1];

	for (i = 0; i < (L - 1) * N + 1; i++)
	{
		counter1[i] = counter2[i] = 0;
	}

	int pathway[L][N];

	m = 0;

	for (i = 0; i < n; i++)
	{
		np = generate_pathway (N, L, pathway, mode);

		counter2[np] ++;

		if (check_duplicate (N, L, pathway) == 1)
			continue;

		p = network_inferred (N, L, pathway, mode, mode1, mode2);

		if (p == 1)
		{
			if (pnflag == 1)
			{
				printf ("c candidate process\n");
                                
				for (j = 0; j < L; j++)
				{
					for (k = 0; k < N; k++)
					{
						printf ("%d ", pathway[j][k]);
					}
					printf ("\n");
				}
			}

			m ++;
			counter1[np] ++;
		}
	}

	for (i = 0; i < (L - 1) * N + 1; i++)
	{
		if (counter2[i])
			printf ("Num of 1 : %8d SATISFIABLE : %ld / %ld = %4.2f\n", i, counter1[i], counter2[i], 100.0 * counter1[i] / counter2[i]);
	}

	printf ("Total %d candidate processes are obtained.\n", m);
}
